#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>


#include <sys/select.h>
#include <poll.h>
#include <sys/epoll.h>


void *client_thread(void *arg) {
    int clientfd = *(int *)arg;
    while (1) {
        char buffer[128] = {0};
        int count = recv(clientfd, buffer, 128, 0);
        if (0 == count) {
            break;
        }
        send(clientfd, buffer, count, 0);
        printf("clientfd: %d, count: %d, buffer: %s\n", clientfd, count, buffer);
    }
    printf("clientfd %d disconnected\n", clientfd);
    close(clientfd);

	return NULL;
}

int main() {
    int socketfd = socket(AF_INET, SOCK_STREAM, 0);
    if (socketfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(struct sockaddr_in));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons(2048);

    if (-1 == bind(socketfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) {
        perror("bind");
        close(socketfd);
        exit(EXIT_FAILURE);
    }

    if (-1 == listen(socketfd, 5)) {
        perror("listen");
        close(socketfd);
        exit(EXIT_FAILURE);
    }

#if 0

    struct sockaddr_in clientaddr;
    socklen_t len = sizeof(clientaddr);
    int clientfd = accept(socketfd, (struct sockaddr *)&clientaddr, &len);
    printf("accept\n");

#if 0
    char buffer[128] = {0};
    int count = recv(clientfd, buffer, 128, 0);
    send(clientfd, buffer, count, 0);
    printf("socket %d, clientfd: %d, count: %d, buffer: %s\n", clientfd, clientfd, count, buffer);

#else
    char buffer[128] = {0};
    int count = recv(clientfd, buffer, 128, 0);

    send(clientfd, buffer, count, 0);
    printf("socket %d, clientfd: %d, count: %d, buffer: %s\n", clientfd, clientfd, count, buffer);

#endif

#elif 0  // pthread
    while (1) {
        struct sockaddr_in clientaddr;
        socklen_t len = sizeof(clientaddr);
        int clientfd = accept(socketfd, (struct sockaddr *)&clientaddr, &len);
        if (clientfd == -1) {
            perror("accept");
            continue;
        }
        pthread_t tid;
        pthread_create(&tid, NULL, client_thread, (void *)&clientfd);
        pthread_detach(tid);
        printf("accept clientfd: %d\n", clientfd);
    }

#elif 0 // select
    /*
    int select(int nfds, fd_set *_Nullable restrict readfds,
                  fd_set *_Nullable restrict writefds,
                  fd_set *_Nullable restrict exceptfds,
                  struct timeval *_Nullable restrict timeout);
    */

    fd_set rfds, rset;
	FD_ZERO(&rfds);
	FD_SET(socketfd, &rfds);

	int maxfd = socketfd;

	while (1) {
		rset = rfds;

		int nready = select(maxfd + 1, &rset, NULL, NULL, NULL);

		if (FD_ISSET(socketfd, &rset)) {
            struct sockaddr_in clientaddr;
            socklen_t len = sizeof(clientaddr);
            int clientfd = accept(socketfd, (struct sockaddr *)&clientaddr, &len);

			printf("clientfd: %d\n", clientfd);
            FD_SET(clientfd, &rfds);
            maxfd = (clientfd > maxfd) ? clientfd : maxfd;

		}
        int i = 0;
        for (i = socketfd + 1; i <= maxfd; i++) {
            if (FD_ISSET(i, &rset)) {
                char buffer[128] = {0};
                int count = recv(i, buffer, 128, 0);
                if (0 == count) {
                    printf("clientfd %d disconnected\n", i);
                    close(i);
                    FD_CLR(i, &rfds);
                } else {
                    send(i, buffer, count, 0);
                    printf("clientfd: %d, count: %d, buffer: %s\n", i, count, buffer);
                }
            }
        }

	}

#elif 0 // poll
    struct pollfd fds[1024] = {0};
    fds[socketfd].fd = socketfd;
    fds[socketfd].events = POLLIN;

    int maxfd = socketfd;

    while (1) {
        int nreeady = poll(fds, maxfd + 1, -1);

        if (fds[socketfd].revents & POLLIN) {
            struct sockaddr_in clientaddr;
            socklen_t len = sizeof(clientaddr);
            int clientfd = accept(socketfd, (struct sockaddr *)&clientaddr, &len);

            printf("clientfd: %d\n", clientfd);
            fds[clientfd].fd = clientfd;
            fds[clientfd].events = POLLIN;
            maxfd = (clientfd > maxfd) ? clientfd : maxfd;
        }
        int i = 0;
        for (i = socketfd + 1; i <= maxfd; i++) {
            if (fds[i].revents & POLLIN) {
                char buffer[128] = {0};
                int count = recv(i, buffer, 128, 0);
                if (0 == count) {
                    printf("clientfd %d disconnected\n", i);
                    close(i);
                    fds[i].fd = -1;
                    fds[i].events = 0;

                } else {
                    send(i, buffer, count, 0);
                    printf("clientfd: %d, count: %d, buffer: %s\n", i, count, buffer);
                }
            }
		}
    }

#else
    int epfd = epoll_create(1);

    struct epoll_event ev, events[1024] = {0};
    ev.events = EPOLLIN;
    ev.data.fd = socketfd;

    epoll_ctl(epfd, EPOLL_CTL_ADD, socketfd, &ev);

    while (1) {
        int nready = epoll_wait(epfd, events, 1024, -1);

        int i = 0;
        for (i = 0; i < nready; i++) {
            int connfd = events[i].data.fd; 
            if (connfd == socketfd) {
                struct sockaddr_in clientaddr;
                socklen_t len = sizeof(clientaddr);
                int clientfd = accept(socketfd, (struct sockaddr *)&clientaddr, &len);

                printf("clientfd: %d\n", clientfd);
                ev.events = EPOLLIN;
                ev.data.fd = clientfd;
                epoll_ctl(epfd, EPOLL_CTL_ADD, clientfd, &ev);
            } else {
                char buffer[128] = {0};
                int count = recv(connfd, buffer, 128, 0);
                if (0 == count) {
                    printf("clientfd %d disconnected\n", connfd);
                    close(connfd);
                    epoll_ctl(epfd, EPOLL_CTL_DEL, connfd, NULL);

                } else {
                    send(connfd, buffer, count, 0);
                    printf("clientfd: %d, count: %d, buffer: %s\n", connfd, count, buffer);
                }
            }
        }
    }
    


#endif

    // close(socketfd);
    return 0;

}
